
package bus

import chisel3._
import chisel3.util._
import device._
import utils._
import common.Constants._

class CoreLinkIOCrossbar1toN(addressSpace: List[(Long, Long)]) extends Module {
  val io = IO(new Bundle {
    val in = Flipped(new CoreLinkIO(XLEN))
    val out = Vec(addressSpace.length, new CoreLinkIO(XLEN))
  })

  val s_idle :: s_resp :: s_error :: Nil = Enum(3)
  val state = RegInit(s_idle)

  // select the output channel according to the address
  val addr = io.in.req.bits.addr
  val outSelVec = VecInit(addressSpace.map(range => (addr >= range._1.U && addr < (range._1 + range._2).U)))
  val outSelIdx = PriorityEncoder(outSelVec)
  val outSel = io.out(outSelIdx)
  val outSelIdxResp = RegEnable(outSelIdx, outSel.req.fire() && (state === s_idle))
  val outSelResp = io.out(outSelIdxResp)
  val reqInvalidAddr = io.in.req.valid && !outSelVec.asUInt.orR

  // when((io.in.req.valid && !outSelVec.asUInt.orR) || (io.in.req.valid && outSelVec.asUInt.andR)){
  //     printf("crossbar access bad addr %x\n", addr)
  // }
  // assert(!(io.in.req.valid && outSelVec.asUInt.andR), "address decode error, bad addr = 0x%x\n", addr)

  // bind out.req channel
  (io.out zip outSelVec).map { case (o, v) => {
    o.req.bits := io.in.req.bits
    o.req.valid := v && (io.in.req.valid && (state === s_idle))
    o.resp.ready := v
  }}

  switch (state) {
    is (s_idle) { 
      when (outSel.req.fire()) { state := s_resp } 
      when (reqInvalidAddr) { state := s_error } 
    }
    is (s_resp) { when (outSelResp.resp.fire()) { state := s_idle } }
    is (s_error) { when(io.in.resp.fire()){ state := s_idle } }
  }

  io.in.resp.valid := outSelResp.resp.fire() || state === s_error
  io.in.resp.bits <> outSelResp.resp.bits
  // io.in.resp.bits.exc.get := state === s_error
  outSelResp.resp.ready := io.in.resp.ready
  io.in.req.ready := outSel.req.ready || reqInvalidAddr
}

/**
 * CoreLinkIO N to 1 crossbar
 * @param n : require num
 */
class CoreLinkIOCrossbarNto1(n: Int, width: Int) extends Module {
  val io = IO(new Bundle(){
    val in = Flipped(Vec(n, new CoreLinkIO(width)))
    val out = new CoreLinkIO(width)
  })

  val idle :: readResp :: writeResp :: Nil = Enum(3)
  val state = RegInit(idle)
  val port = RegInit(0.U(log2Up(n).W))

  val selectReq = Wire(Decoupled(new CoreReqIO(width)))
  val chosen = Wire(UInt(n.W))
  for (i <-0 until n) {
    when(io.in(i).req.valid){
      chosen := i.U
      selectReq := io.in(i).req
    }
  }

  io.out.req.valid := selectReq.valid
  io.out.req.bits := selectReq.bits
  selectReq.ready := io.out.req.ready && (state === idle)

  io.in.map(_.resp.bits := io.out.resp.bits)
  io.in.map(_.resp.valid := false.B)

  io.in(port).resp.valid := io.out.resp.valid
  io.out.resp.ready := io.in(port).resp.ready

  /**
   * state
   */
  switch(state) {
    is(idle){
      when(selectReq.fire()) {
        port := chosen
        when(selectReq.bits.isRead()) {
          state := readResp
        }.elsewhen (selectReq.bits.isWrite()) {
          state := writeResp
        }
      }
    }
    is(readResp) {
      when (io.out.resp.fire() && io.out.resp.bits.isReadLast()) {
        state := idle
      }
    }
    is(writeResp) {
      when (io.out.resp.fire()) { state := idle }
    }
  }

}